Skip to content

feat(ux): inline editor + visual grouper + scroll fixes for meus treinos#175

Merged
EmiyaKiritsugu3 merged 15 commits into
mainfrom
fix/meus-treinos-ux-improvements
Jul 5, 2026
Merged

feat(ux): inline editor + visual grouper + scroll fixes for meus treinos#175
EmiyaKiritsugu3 merged 15 commits into
mainfrom
fix/meus-treinos-ux-improvements

Conversation

@EmiyaKiritsugu3

@EmiyaKiritsugu3 EmiyaKiritsugu3 commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Changes

  • WorkoutEditor renders inline inside treino cards (no scroll jump)
  • Visual grouper banner after AI weekly plan generation (auto-hides 30s)
  • After-gen smooth scroll to treino list with rAF retry
  • Create button moved above treino list, opens inline editor
  • Removed bottom-of-page editor
  • Resets planName on each generation
  • planName exposed from useWorkoutGeneration

Files (6)

  • workout-editor.tsx — compact prop for inline rendering
  • use-workout-generation.ts — exposes planName, resets on gen
  • meus-treinos-client.tsx — inline editor, banner, rAF scroll, repositioned button
  • use-workout-crud.ts — removed scrollTo, optional treinoId param
  • workout-editor.test.tsx — compact mode tests
  • meus-treinos-client.test.tsx — planName banner test
  • use-workout-crud.test.ts — test fixes for API changes

Review

  • Final branch review passed: 1 critical + 2 important fixed
  • All task reviews clean (spec ✅, quality approved)
  • 5 TDD tasks + 3 fix commits = 8 commits total

Closes UX issues from design spec 2026-07-05.

🤖 Generated with Claude Code


Summary by cubic

Inline editor inside treino cards, a short “Plano Semanal” banner after generation, and smooth scroll back to the treino list. The create button now sits above the list; the bottom editor is removed, with sturdier auth and error handling.

  • New Features

    • WorkoutEditor adds compact and renders inline in treino cards; create opens inline above the list.
    • Banner shows the generated plan name for 30s; use-workout-generation exposes and resets planName.
  • Bug Fixes

    • Smooth scroll to #treinos-pessoais after generation with rAF retry capped at 20; removed window.scrollTo.
    • Editor stays open on save failure; closes only after a successful save.
    • Fixed auth in updateTreinoDayAction and deleteTreinoAction by resolving aluno.id; handleDayChange/handleDelete now surface server errors.
    • Made onSuccess optional in use-workout-generation to resolve typing issues in tests.

Written for commit 2133cfe. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added inline editing for training cards, including a compact editor view for creating or updating workouts directly in the list.
    • Added a weekly plan banner that shows when a generated plan is available.
    • After generating a workout, the page now refreshes and smoothly returns to the training list.
  • Bug Fixes

    • Improved edit/delete behavior and error handling for workout changes.
    • Updated workout ownership checks to better protect account-specific actions.

EmiyaKiritsugu3 and others added 11 commits July 5, 2026 11:08
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
- Add compact?: boolean prop (default false) to WorkoutEditor
- Extract JSX into const content, conditionally wrap in div vs Card
- Add 2 tests: compact omits Card, default renders Card

Co-Authored-By: Claude <noreply@anthropic.com>
- editingTreinoId state drives inline WorkoutEditor per card
- handleEditLocal replaces scrollTo behavior
- Criar Manual button moved above list, opens inline editor
- Removed bottom-of-page conditional editor
- Removed window.scrollTo from use-workout-crud

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Ensures banner re-triggers on every AI gen, not just first time.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces fixed 300ms setTimeout with rAF loop that retries
until #treinos-pessoais anchor exists in DOM. Eliminates
race condition on slow devices / slow RSC renders.

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smartmanagementsystem Ready Ready Preview, Comment Jul 5, 2026 4:35pm

@ecc-tools

ecc-tools Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds a compact prop to WorkoutEditor, exposes planName from useWorkoutGeneration, and refactors meus-treinos-client.tsx to use inline per-card editing driven by editingTreinoId, a "Plano Semanal" banner, and post-generation scroll-to-list behavior. handleSave now takes an explicit treinoId. Treino ownership checks in server actions now use resolveAlunoId. Includes planning/design docs and test updates.

Changes

Meus Treinos UX and Backend Fixes

Layer / File(s) Summary
WorkoutEditor compact mode
src/components/dashboard/aluno/workout-editor.tsx, src/components/dashboard/aluno/workout-editor.test.tsx
Adds optional compact prop that renders shared content without the outer Card wrapper; tests cover both modes.
useWorkoutGeneration planName
src/hooks/use-workout-generation.ts
Adds planName state, reset on generate start, set from generation result, and returned from the hook.
useWorkoutCRUD handleSave/handleEdit refactor
src/hooks/use-workout-crud.ts, src/hooks/use-workout-crud.test.ts
handleSave now accepts explicit treinoId to determine edit/create; handleEdit no longer scrolls; error paths surface server error messages; tests updated.
Inline editor and generation banner
src/app/aluno/meus-treinos/meus-treinos-client.tsx, meus-treinos-client.test.tsx
Renders inline WorkoutEditor per card via editingTreinoId, repositions manual-create button, adds inline new-treino mode, shows "Plano Semanal" banner from planName, and scrolls to the treino list after generation.
Aluno ownership checks
src/lib/actions/treinos.ts
updateTreinoDayAction and deleteTreinoAction compare treino.alunoId against resolveAlunoId(user) instead of user.id.
Planning/design docs
docs/superpowers/plans/*, docs/superpowers/specs/*
Adds implementation plan and design spec for the above UX changes.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Student
  participant MeusTreinosClient
  participant useWorkoutGeneration
  participant Router

  Student->>MeusTreinosClient: click Gerar Treino
  MeusTreinosClient->>useWorkoutGeneration: handleGenerate()
  useWorkoutGeneration-->>MeusTreinosClient: onSuccess(planName)
  MeusTreinosClient->>Router: router.refresh()
  MeusTreinosClient->>MeusTreinosClient: show Plano Semanal banner
  MeusTreinosClient->>MeusTreinosClient: scroll to treinos-pessoais (rAF retry)
  MeusTreinosClient->>MeusTreinosClient: auto-hide banner after 30s
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main UX changes: inline editor, visual banner, and scroll fixes for Meus Treinos.
Description check ✅ Passed The description is detailed and relevant, but it omits the template's Type of Change, Related Documents, and checklist sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/meus-treinos-ux-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/app/aluno/meus-treinos/meus-treinos-client.tsx">

<violation number="1" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:138">
P2: When a save request fails, the inline editor is still closed immediately because `setEditingTreinoId(null)` runs right after calling `handleSave(...)` without waiting for success. This regresses the previous behavior where the form stayed open on error, so users can lose what they just typed. A safer flow is to close the inline editor only after a confirmed successful save result.</violation>

<violation number="2" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:277">
P2: Custom agent: **Enforce Strict Maintainability Standards**

The `__new__` magic-string sentinel overloads `editingTreinoId` to mean both "editing an existing treino" (when it holds a real ID) and "creating a new treino" (when it equals `__new__`). A `string | null` type can only express two states, so the third state is hidden by an implicit convention that every reader must discover. Replacing this with a discriminated union or a dedicated boolean flag would make the state machine explicit and self-documenting, and remove the risk of the sentinel drifting out of sync if it is ever changed in one place but not the other.</violation>
</file>

<file name="src/hooks/use-workout-generation.ts">

<violation number="1" location="src/hooks/use-workout-generation.ts:81">
P2: `planName` state is set inside the success block but never cleaned up when re-invocation fails mid-way. If a second generation is started while the first is still in-flight, `setPlanName(null)` runs immediately but the in-flight success could set `planName` to stale data after the second generation completes. Consider using an abort/mount ref or resetting `planName` only at the start of a new generation (already done) and protecting `setPlanName` from stale completions.</violation>
</file>

<file name="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md">

<violation number="1" location="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md:9">
P3: The implementation plan now documents `React 18`, but this repository is on React 19.x. That mismatch can lead to incorrect implementation/testing guidance (especially around runtime/test behavior), so updating the plan to the actual version would keep the UX task instructions reliable.</violation>

<violation number="2" location="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md:337">
P1: Test mock setup is broken: Step 1 replaces `useWorkoutGeneration: vi.fn(() => ({...}))` with `useWorkoutGeneration: () => ({...})`, dropping the `vi.fn()` wrapper. Then Step 3 attempts `(useWorkoutGeneration as ReturnType<typeof vi.fn>).mockReturnValue(...)` — but since `useWorkoutGeneration` is now a plain arrow function, `.mockReturnValue` is undefined and the test will throw a TypeError at runtime. Keep `vi.fn(() => ...)` in the mock factory so the dynamic override in the banner test works. The `as ReturnType<typeof vi.fn>` cast only silences TypeScript, it doesn't create the mock method at runtime.</violation>
</file>

<file name="src/hooks/use-workout-crud.ts">

<violation number="1" location="src/hooks/use-workout-crud.ts:28">
P2: `editingTreino` and `isFormVisible` state inside `useWorkoutCRUD` is now maintained but unused after the refactor. The consumer (`meus-treinos-client.tsx`) manages its own `editingTreinoId` state and doesn't destructure these from the hook. `handleEdit` still sets them and `handleSave` still resets them, but nothing reads them — creates unnecessary state management overhead and could mislead future maintainers into thinking the hook controls form visibility. Suggest removing `editingTreino`, `isFormVisible`, and their setters from the hook since the consumer owns inline editing state externally.</violation>
</file>

<file name="docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md">

<violation number="1" location="docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md:69">
P2: The design doc now points implementation to `card-treino.tsx`, while the implementation plan explicitly says inline editing must not be done there. This conflict can send contributors to modify the wrong component path, so aligning both docs to the same target file(s) would avoid rework.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


```typescript
// Lines 57-62, add planName to hook mock return:
vi.mock('@/hooks/use-workout-generation', () => ({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Test mock setup is broken: Step 1 replaces useWorkoutGeneration: vi.fn(() => ({...})) with useWorkoutGeneration: () => ({...}), dropping the vi.fn() wrapper. Then Step 3 attempts (useWorkoutGeneration as ReturnType<typeof vi.fn>).mockReturnValue(...) — but since useWorkoutGeneration is now a plain arrow function, .mockReturnValue is undefined and the test will throw a TypeError at runtime. Keep vi.fn(() => ...) in the mock factory so the dynamic override in the banner test works. The as ReturnType<typeof vi.fn> cast only silences TypeScript, it doesn't create the mock method at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md, line 337:

<comment>Test mock setup is broken: Step 1 replaces `useWorkoutGeneration: vi.fn(() => ({...}))` with `useWorkoutGeneration: () => ({...})`, dropping the `vi.fn()` wrapper. Then Step 3 attempts `(useWorkoutGeneration as ReturnType<typeof vi.fn>).mockReturnValue(...)` — but since `useWorkoutGeneration` is now a plain arrow function, `.mockReturnValue` is undefined and the test will throw a TypeError at runtime. Keep `vi.fn(() => ...)` in the mock factory so the dynamic override in the banner test works. The `as ReturnType<typeof vi.fn>` cast only silences TypeScript, it doesn't create the mock method at runtime.</comment>

<file context>
@@ -0,0 +1,431 @@
+
+```typescript
+// Lines 57-62, add planName to hook mock return:
+vi.mock('@/hooks/use-workout-generation', () => ({
+  useWorkoutGeneration: () => ({
+    isGenerating: false,
</file context>

<div className="text-center mt-4">
<Button
onClick={() => {
setEditingTreinoId('__new__');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Enforce Strict Maintainability Standards

The __new__ magic-string sentinel overloads editingTreinoId to mean both "editing an existing treino" (when it holds a real ID) and "creating a new treino" (when it equals __new__). A string | null type can only express two states, so the third state is hidden by an implicit convention that every reader must discover. Replacing this with a discriminated union or a dedicated boolean flag would make the state machine explicit and self-documenting, and remove the risk of the sentinel drifting out of sync if it is ever changed in one place but not the other.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/meus-treinos/meus-treinos-client.tsx, line 277:

<comment>The `__new__` magic-string sentinel overloads `editingTreinoId` to mean both "editing an existing treino" (when it holds a real ID) and "creating a new treino" (when it equals `__new__`). A `string | null` type can only express two states, so the third state is hidden by an implicit convention that every reader must discover. Replacing this with a discriminated union or a dedicated boolean flag would make the state machine explicit and self-documenting, and remove the risk of the sentinel drifting out of sync if it is ever changed in one place but not the other.</comment>

<file context>
@@ -201,44 +246,57 @@ export default function MeusTreinosClient({
+        <div className="text-center mt-4">
+          <Button
+            onClick={() => {
+              setEditingTreinoId('__new__');
+            }}
+            variant="outline"
</file context>

Comment thread src/app/aluno/meus-treinos/meus-treinos-client.tsx Outdated
}
}

setPlanName(result.planName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: planName state is set inside the success block but never cleaned up when re-invocation fails mid-way. If a second generation is started while the first is still in-flight, setPlanName(null) runs immediately but the in-flight success could set planName to stale data after the second generation completes. Consider using an abort/mount ref or resetting planName only at the start of a new generation (already done) and protecting setPlanName from stale completions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/hooks/use-workout-generation.ts, line 81:

<comment>`planName` state is set inside the success block but never cleaned up when re-invocation fails mid-way. If a second generation is started while the first is still in-flight, `setPlanName(null)` runs immediately but the in-flight success could set `planName` to stale data after the second generation completes. Consider using an abort/mount ref or resetting `planName` only at the start of a new generation (already done) and protecting `setPlanName` from stale completions.</comment>

<file context>
@@ -76,6 +78,7 @@ export function useWorkoutGeneration({
             }
           }
 
+          setPlanName(result.planName);
           notify.success('Plano Pessoal Gerado!', `${result.planName} foi criado com sucesso.`);
           onSuccess();
</file context>

Comment thread src/hooks/use-workout-crud.ts

| File | Change | Risk |
|---|---|---|
| `card-treino.tsx` | +isEditing state, inline WorkoutEditor render | Low |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The design doc now points implementation to card-treino.tsx, while the implementation plan explicitly says inline editing must not be done there. This conflict can send contributors to modify the wrong component path, so aligning both docs to the same target file(s) would avoid rework.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md, line 69:

<comment>The design doc now points implementation to `card-treino.tsx`, while the implementation plan explicitly says inline editing must not be done there. This conflict can send contributors to modify the wrong component path, so aligning both docs to the same target file(s) would avoid rework.</comment>

<file context>
@@ -0,0 +1,88 @@
+
+| File | Change | Risk |
+|---|---|---|
+| `card-treino.tsx` | +isEditing state, inline WorkoutEditor render | Low |
+| `workout-editor.tsx` | +`compact?: boolean` prop | Low |
+| `meus-treinos-client.tsx` | -bottom editor, +banner, +planName, +scroll anchor | Medium |
</file context>


**Architecture:** State `editingTreinoId` drives which card shows inline `WorkoutEditor`. `WorkoutEditor` gets `compact` prop to drop outer Card wrapper. `useWorkoutGeneration` exposes `planName`. Banner + scroll driven by local state in `meus-treinos-client.tsx`.

**Tech Stack:** React 18, TypeScript 5, Next.js 15 App Router, Vitest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The implementation plan now documents React 18, but this repository is on React 19.x. That mismatch can lead to incorrect implementation/testing guidance (especially around runtime/test behavior), so updating the plan to the actual version would keep the UX task instructions reliable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md, line 9:

<comment>The implementation plan now documents `React 18`, but this repository is on React 19.x. That mismatch can lead to incorrect implementation/testing guidance (especially around runtime/test behavior), so updating the plan to the actual version would keep the UX task instructions reliable.</comment>

<file context>
@@ -0,0 +1,431 @@
+
+**Architecture:** State `editingTreinoId` drives which card shows inline `WorkoutEditor`. `WorkoutEditor` gets `compact` prop to drop outer Card wrapper. `useWorkoutGeneration` exposes `planName`. Banner + scroll driven by local state in `meus-treinos-client.tsx`.
+
+**Tech Stack:** React 18, TypeScript 5, Next.js 15 App Router, Vitest
+
+## Global Constraints
</file context>

…rization

deleteTreinoAction and updateTreinoDayAction compared treino.alunoId
(Prisma ID) against user.id (Auth UUID), always mismatching after
PR #174 FK fix. Resolve aluno.id by email before auth check.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/hooks/use-workout-generation.ts">

<violation number="1" location="src/hooks/use-workout-generation.ts:81">
P2: `planName` state is set inside the success block but never cleaned up when re-invocation fails mid-way. If a second generation is started while the first is still in-flight, `setPlanName(null)` runs immediately but the in-flight success could set `planName` to stale data after the second generation completes. Consider using an abort/mount ref or resetting `planName` only at the start of a new generation (already done) and protecting `setPlanName` from stale completions.</violation>
</file>

<file name="src/app/aluno/meus-treinos/meus-treinos-client.tsx">

<violation number="1" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:277">
P2: Custom agent: **Enforce Strict Maintainability Standards**

The `__new__` magic-string sentinel overloads `editingTreinoId` to mean both "editing an existing treino" (when it holds a real ID) and "creating a new treino" (when it equals `__new__`). A `string | null` type can only express two states, so the third state is hidden by an implicit convention that every reader must discover. Replacing this with a discriminated union or a dedicated boolean flag would make the state machine explicit and self-documenting, and remove the risk of the sentinel drifting out of sync if it is ever changed in one place but not the other.</violation>
</file>

<file name="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md">

<violation number="1" location="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md:9">
P3: The implementation plan now documents `React 18`, but this repository is on React 19.x. That mismatch can lead to incorrect implementation/testing guidance (especially around runtime/test behavior), so updating the plan to the actual version would keep the UX task instructions reliable.</violation>

<violation number="2" location="docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md:337">
P1: Test mock setup is broken: Step 1 replaces `useWorkoutGeneration: vi.fn(() => ({...}))` with `useWorkoutGeneration: () => ({...})`, dropping the `vi.fn()` wrapper. Then Step 3 attempts `(useWorkoutGeneration as ReturnType<typeof vi.fn>).mockReturnValue(...)` — but since `useWorkoutGeneration` is now a plain arrow function, `.mockReturnValue` is undefined and the test will throw a TypeError at runtime. Keep `vi.fn(() => ...)` in the mock factory so the dynamic override in the banner test works. The `as ReturnType<typeof vi.fn>` cast only silences TypeScript, it doesn't create the mock method at runtime.</violation>
</file>

<file name="docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md">

<violation number="1" location="docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md:69">
P2: The design doc now points implementation to `card-treino.tsx`, while the implementation plan explicitly says inline editing must not be done there. This conflict can send contributors to modify the wrong component path, so aligning both docs to the same target file(s) would avoid rework.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/lib/actions/treinos.ts Outdated
- rAF scroll capped at 20 attempts (prevents infinite loop)
- handleDelete + handleDayChange: surface server errors to user
- replace inline prisma.aluno.findUnique with resolveAlunoId() for auth
- add onSuccess to useCallback dep array

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

Pre-existing tests don't pass onSuccess — made optional to avoid
12 TS2345 errors. Caller (meus-treinos-client) always passes it.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/hooks/use-workout-crud.ts (1)

20-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate edit-state ownership in useWorkoutCRUD Only MeusTreinosClient consumes this hook now, and it keeps editingTreinoId locally. The hook’s isFormVisible/editingTreino/handleEdit state is redundant and can be removed or folded into a single source of truth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/use-workout-crud.ts` around lines 20 - 25, In useWorkoutCRUD,
remove the redundant edit-form state owned by the hook: isFormVisible,
editingTreino, and handleEdit should no longer be separate sources of truth
since MeusTreinosClient now tracks editingTreinoId locally. Update
useWorkoutCRUD to keep only the CRUD state it still owns, and adjust any
returned values or handlers so edit behavior is driven from the client
component’s single edit-state flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/aluno/meus-treinos/meus-treinos-client.tsx`:
- Around line 82-92: The plan banner can remain visible forever if a new
generation starts and then fails because the existing timer is cleared by the
`useEffect` cleanup when `planName` becomes null. Update
`meus-treinos-client.tsx` so the banner is explicitly hidden at the start of the
generation flow (for example in `onGenerate` or right before `handleGenerate`
runs) by resetting both `setShowPlanBanner(false)` and
`setBannerPlanName(null)`, instead of relying only on the `planName` effect in
`useEffect`.

---

Nitpick comments:
In `@src/hooks/use-workout-crud.ts`:
- Around line 20-25: In useWorkoutCRUD, remove the redundant edit-form state
owned by the hook: isFormVisible, editingTreino, and handleEdit should no longer
be separate sources of truth since MeusTreinosClient now tracks editingTreinoId
locally. Update useWorkoutCRUD to keep only the CRUD state it still owns, and
adjust any returned values or handlers so edit behavior is driven from the
client component’s single edit-state flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a09edfef-b533-464c-9bd0-cf5979a7e387

📥 Commits

Reviewing files that changed from the base of the PR and between 1118d92 and 2c65f93.

📒 Files selected for processing (10)
  • docs/superpowers/plans/2026-07-05-meus-treinos-ux-improvements.md
  • docs/superpowers/specs/2026-07-05-meus-treinos-ux-improvements-design.md
  • src/app/aluno/meus-treinos/meus-treinos-client.test.tsx
  • src/app/aluno/meus-treinos/meus-treinos-client.tsx
  • src/components/dashboard/aluno/workout-editor.test.tsx
  • src/components/dashboard/aluno/workout-editor.tsx
  • src/hooks/use-workout-crud.test.ts
  • src/hooks/use-workout-crud.ts
  • src/hooks/use-workout-generation.ts
  • src/lib/actions/treinos.ts

Comment on lines +82 to +92
const [showPlanBanner, setShowPlanBanner] = useState(false);
const [bannerPlanName, setBannerPlanName] = useState<string | null>(null);

useEffect(() => {
if (planName) {
setBannerPlanName(planName);
setShowPlanBanner(true);
const timer = setTimeout(() => setShowPlanBanner(false), 30000);
return () => clearTimeout(timer);
}
}, [planName]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Banner can get stuck if a subsequent generation attempt fails.

Starting a new handleGenerate call resets planName to null, which triggers this effect's cleanup and cancels the pending 30s auto-hide timer for the currently visible banner — but nothing re-arms it if the new generation then fails. The banner (with the stale plan name) will stay visible indefinitely with no way to auto-clear.

🐛 Suggested fix: explicitly hide the banner when a new generation starts
+  const [isRegenerating, setIsRegenerating] = useState(false);
+
   useEffect(() => {
     if (planName) {
       setBannerPlanName(planName);
       setShowPlanBanner(true);
       const timer = setTimeout(() => setShowPlanBanner(false), 30000);
       return () => clearTimeout(timer);
+    } else if (isGenerating) {
+      setShowPlanBanner(false);
     }
-  }, [planName]);
+  }, [planName, isGenerating]);

As an alternative, simply hide the banner explicitly at the start of onGenerate/before calling handleGenerate, rather than relying solely on the planName reset to null.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/aluno/meus-treinos/meus-treinos-client.tsx` around lines 82 - 92, The
plan banner can remain visible forever if a new generation starts and then fails
because the existing timer is cleared by the `useEffect` cleanup when `planName`
becomes null. Update `meus-treinos-client.tsx` so the banner is explicitly
hidden at the start of the generation flow (for example in `onGenerate` or right
before `handleGenerate` runs) by resetting both `setShowPlanBanner(false)` and
`setBannerPlanName(null)`, instead of relying only on the `planName` effect in
`useEffect`.

handleSave returns boolean. Client closes editor only on success.
Removed unused isFormVisible, editingTreino, handleEdit from hook.
Kept __new__ sentinel for now.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/app/aluno/meus-treinos/meus-treinos-client.tsx">

<violation number="1" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:142">
P2: The async `onSave` callback unconditionally clears the global `editingTreinoId` after a successful save. Because other treino cards remain interactive during the save request, a user could switch to editing a different treino before the first save resolves. When the late success fires, it will close the wrong inline editor and potentially discard in-progress edits.

A safer pattern is to clear the editor only if the same treino is still being edited by using a functional state update.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

treinoToEdit={treino}
onSave={async (data) => {
const ok = await handleSave(data, treino.id);
if (ok) setEditingTreinoId(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The async onSave callback unconditionally clears the global editingTreinoId after a successful save. Because other treino cards remain interactive during the save request, a user could switch to editing a different treino before the first save resolves. When the late success fires, it will close the wrong inline editor and potentially discard in-progress edits.

A safer pattern is to clear the editor only if the same treino is still being edited by using a functional state update.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/meus-treinos/meus-treinos-client.tsx, line 142:

<comment>The async `onSave` callback unconditionally clears the global `editingTreinoId` after a successful save. Because other treino cards remain interactive during the save request, a user could switch to editing a different treino before the first save resolves. When the late success fires, it will close the wrong inline editor and potentially discard in-progress edits.

A safer pattern is to clear the editor only if the same treino is still being edited by using a functional state update.</comment>

<file context>
@@ -137,9 +137,9 @@ export default function MeusTreinosClient({
-                    setEditingTreinoId(null);
+                  onSave={async (data) => {
+                    const ok = await handleSave(data, treino.id);
+                    if (ok) setEditingTreinoId(null);
                   }}
                   onCancel={() => setEditingTreinoId(null)}
</file context>
Suggested change
if (ok) setEditingTreinoId(null);
if (ok) setEditingTreinoId((current) => (current === treino.id ? null : current));

@EmiyaKiritsugu3 EmiyaKiritsugu3 merged commit fac628b into main Jul 5, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant